#include"MatrixVectorRead.h"
#include <assert.h>
#include<mpi.h>
#include<stdlib.h>
#include <iostream>
#include<papi.h>
using namespace std;


int main(int argc, char* argv[]){
  int np;
  int rank;
  int ierr;
  char* matrixFile;
  char* vectorFile;
  char* output;
  MPI_Status status;
  MatrixCell* m;
  int* vti;
  double* vtd;
  MatrixCell* vl;
  int vlc;
  int pon;
  int* vc;
  double* v;
  double* pr;
  double* r;
  MatrixCell* tr;
  unsigned long chkflg = PAPI_VER_CURRENT;
  unsigned long start, end;
  // MPI init
  MPI_Init(&argc, &argv);
  matrixFile=argv[1];
  vectorFile=argv[2];
  output=argv[3];
  MPI_Comm_size(MPI_COMM_WORLD, &np);
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  vc=new int[np];
  /*init papi*/
  PAPI_library_init(chkflg);
  if (chkflg != PAPI_VER_CURRENT) {
    printf("Error PAPI Library out of date\n");
    exit(1);
  }
  readMatrixVector(matrixFile, vectorFile, rank, np);
  //Timer
  /*start timing*/
  if(rank==0){
    start = PAPI_get_real_usec();
  }
  m=new MatrixCell[getMatrixSize()];
  m=getMatrix();
  vlc=getVectorSize();
  vc[rank]=vlc;
  vl=new MatrixCell[getVectorSize()];
  vl=getVector();
  //v=new double[getVectorRowSize()];
  v=(double*)calloc(getVectorRowSize(),sizeof(double));
  vti=new int[getVectorRowSize()];
  vtd=new double[getVectorRowSize()];
  pr=(double*)calloc(getMatrixRowSize(),sizeof(double));
  r=new double[getMatrixRowSize()];
  tr=new MatrixCell[getMatrixRowSize()];
  for(int i=0;i<np;i++){
    MPI_Bcast(vc+i,1,MPI_INT,i,MPI_COMM_WORLD);
  }
  pon=0;
  for(int i=0;i<np;i++){
    if(rank==i){
      for(int j=0;j<vlc;j++){
        vti[pon+j]=vl[j].row;
        vtd[pon+j]=vl[j].value;
      }
    }else{
      pon+=vc[i];
    }
  }
  pon=0;
  for(int i=0;i<np;i++){
    MPI_Bcast(vti+pon,vc[i],MPI_INT,i,MPI_COMM_WORLD);
    MPI_Bcast(vtd+pon,vc[i],MPI_DOUBLE,i,MPI_COMM_WORLD);
    pon+=vc[i];
  }
  for(int i=0;i<pon;i++){
    v[vti[i]-1]=vtd[i];
  }
  for(int i=0;i<getMatrixSize();i++){
    pr[m[i].row-1]+=m[i].value*v[m[i].col-1];
  }
  MPI_Reduce(pr,r,getMatrixRowSize(),MPI_DOUBLE,MPI_SUM,0,MPI_COMM_WORLD);
  pon=0;
  if(rank==0){
    for(int i=0;i<getMatrixRowSize();i++){
      if(r[i]!=0){
        tr[pon].row=i+1;
        tr[pon].col=1;
        tr[pon].value=r[i];
        pon++;
      }
    }
    end = PAPI_get_real_usec();
    writeOutputVector(output, tr, getMatrixRowSize(), 1, pon);
    printf("Time: %ld\n", end-start);
  } 
  MPI_Finalize();
  return 0;
}
